/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */

package com.hbzhit.modules.act.service.impl;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hbzhit.common.utils.Constant;
import com.hbzhit.common.utils.PageUtils;
import com.hbzhit.common.utils.WorkflowUtils;
import com.hbzhit.modules.act.dto.ProcessDefDTO;
import com.hbzhit.modules.act.enums.ResourceTypeEnum;
import com.hbzhit.modules.act.service.ProcessService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

/**
 * @author lengleng
 * @date 2018/9/25
 */
@Slf4j
@Service
@AllArgsConstructor
public class ProcessServiceImpl implements ProcessService {
    private final RepositoryService repositoryService;
    private final RuntimeService runtimeService;

    @Override
    public PageUtils getProcessByPage(Map<String, Object> params) {
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery()
                // .processDefinitionTenantId(String.valueOf(TenantContextHolder.getTenantId()))
                .latestVersion();

        String category = MapUtil.getStr(params, "category");
        if (StrUtil.isNotBlank(category)) {
            query.processDefinitionCategoryLike('%' + category + '%');
        }

        String name = MapUtil.getStr(params, "name");
        if (StrUtil.isNotBlank(name)) {
            query.processDefinitionNameLike('%' + name + '%');
        }

        int page = MapUtil.getInt(params, Constant.PAGE);
        int limit = MapUtil.getInt(params, Constant.LIMIT);

        IPage<ProcessDefDTO> result = new Page<>(page, limit);
        result.setTotal(query.count());

        List<ProcessDefDTO> deploymentList = query.listPage((page - 1) * limit, limit)
                .stream()
                .map(processDefinition -> {
                    Deployment deployment = repositoryService.createDeploymentQuery()
                            .deploymentId(processDefinition.getDeploymentId()).singleResult();
                    return ProcessDefDTO.toProcessDefDTO(processDefinition, deployment);
                }).collect(Collectors.toList());
        result.setRecords(deploymentList);
        return new PageUtils(result);
    }

    @Override
    public Boolean importBPMN(MultipartFile file) {
        try {
            String fileName = file.getOriginalFilename();
            InputStream e = file.getInputStream();
            Deployment deployment = null;
            String extension = FilenameUtils.getExtension(fileName);
            if (!extension.equals("zip") && !extension.equals("bar")) {
                deployment = this.repositoryService.createDeployment().addInputStream(fileName, e).deploy();
            } else {
                ZipInputStream list = new ZipInputStream(e);
                deployment = this.repositoryService.createDeployment().addZipInputStream(list).deploy();
            }

            List<?> list1 = this.repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
            Iterator<?> var8 = list1.iterator();

            while (var8.hasNext()) {
                ProcessDefinition processDefinition = (ProcessDefinition) var8.next();
                WorkflowUtils.exportDiagramToFile(this.repositoryService, processDefinition, "/tmp/process");
            }
            return true;
        } catch (Exception var9) {
            log.error("导入BPNM失败！", var9);
            return false;
        }
    }

    @Override
    public Boolean convertToModel(String procDefId) {
        try {
            ProcessDefinition processDefinition = (ProcessDefinition) this.repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId).singleResult();
            InputStream bpmnStream = this.repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
            XMLInputFactory xif = XMLInputFactory.newInstance();
            InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
            XMLStreamReader xtr = xif.createXMLStreamReader(in);
            BpmnModel bpmnModel = (new BpmnXMLConverter()).convertToBpmnModel(xtr);
            BpmnJsonConverter converter = new BpmnJsonConverter();
            ObjectNode modelNode = converter.convertToJson(bpmnModel);
            Model modelData = this.repositoryService.newModel();
            modelData.setKey(processDefinition.getKey());
            modelData.setName(processDefinition.getResourceName());
            modelData.setCategory(processDefinition.getDeploymentId());
            ObjectNode modelObjectNode = (new ObjectMapper()).createObjectNode();
            modelObjectNode.put("name", processDefinition.getName());
            modelObjectNode.put("revision", 1);
            modelObjectNode.put("description", processDefinition.getDescription());
            modelData.setMetaInfo(modelObjectNode.toString());
            this.repositoryService.saveModel(modelData);
            this.repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
            return true;
        } catch (Exception e) {
            log.error("转换模型失败！", e);
            return false;
        }
    }

    @Override
    public InputStream readResource(String procDefId, String proInsId, String resType) {

        if (StrUtil.isBlank(procDefId)) {
            ProcessInstance processInstance = runtimeService
                    .createProcessInstanceQuery()
                    .processInstanceId(proInsId)
                    .singleResult();
            procDefId = processInstance.getProcessDefinitionId();
        }
        ProcessDefinition processDefinition = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionId(procDefId)
                .singleResult();

        if (ObjectUtil.isEmpty(processDefinition)) {
            return null;
        }

        String resourceName = "";
        if (ResourceTypeEnum.IMAGE.getType().equals(resType)) {
            resourceName = processDefinition.getDiagramResourceName();
        } else if (ResourceTypeEnum.XML.getType().equals(resType)) {
            resourceName = processDefinition.getResourceName();
        }

        InputStream resourceAsStream = repositoryService
                .getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
        return resourceAsStream;
    }

    @Override
    public Boolean removeProcIns(String deploymentId) {
        repositoryService.deleteDeployment(deploymentId, true);
        return Boolean.TRUE;
    }

}
